-
Notifications
You must be signed in to change notification settings - Fork 77
New issue
Have a question about this project? Sign up for a free GitHub account to open an issue and contact its maintainers and the community.
By clicking “Sign up for GitHub”, you agree to our terms of service and privacy statement. We’ll occasionally send you account related emails.
Already on GitHub? Sign in to your account
Widening gas #1442
base: master
Are you sure you want to change the base?
Widening gas #1442
Conversation
Why does this need to be inside the solver? This sounds like widening delay, which can be completely solver-independent and can be implemented simply as a lifting of a domain (overriding its |
@sim642 One could implement a similar feature as a domain lifter, yes. However, the effect would be subtly different: Here the delay (gas) is applied per unknown, whereas tracking it in the domain would mean that the gas value is propagated, e.g., between different loops, or that a complicated mechanism tracking not just one gas value but several inside a map would be required. |
I think the standard implementation is just domain lifting where transfer functions set the counter to 0, so none of such propagation occurs. I'm again worried about the monolithic growth of the TD3 solver. For a long time we've wanted to undo that monolithic structure into a more modular one. While nobody has worked on decomposing the existing solver, at some point we decided to not worsen the problem and implement new things modularly. For example, |
This seems like a policy discussion, so I cannot really say much. |
I think it would even be easier: one can choose whether to wrap an analysis's |
We discussed this during GobCon and here's the summary. Currently, The change from Some small style improvements should also be done to make this merge-ready. It was mentioned that there are some parentheses which are unneeded (e.g. around |
I have extracted the modules out of td3.Base. For now, I have placed them in their own module in
So far, no other solver implements these strategies, so I have left the selection of the concrete implementation in td3. In their current form, I would not say that the strategies are really independent from td3. Some of the functions/strategies need td3-specific details, like an |
src/solver/td3.ml
Outdated
(* is there a reason for the redundant widen check? *) | ||
if tracing && not (S.Dom.is_bot old) && should_widen x then trace "solchange" "%a (wpx: %b): %a" S.Var.pretty_trace x (should_widen x) S.Dom.pretty_diff (wpd, old); |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
What is this comment about?
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
My comment is strangely worded, but I think it is odd that should_widen
or previously HM.mem wpoint x
is re-evaluated here.
Specifically, let wp = should_widen x
before evaluating eq
is used to determine, whether widening should be applied. The result of should_widen
may have changed through eq
, yet I would assume the value that is logged should reflect the value that is used for the actual widening decision.
But if using the updated value, I should be logging with format_wpoint
, which will not only show whether x is a wpoint
, but also how much gas it has.
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
Reusing wp
seems sensible here indeed.
It would've been fine to leave them in |
for globals unknowns: * can be used together with other widening strategies * reduce gas whenever global grows * widen only if marked as widening point by side_widen and gas is 0
b65cdc3
to
493732e
Compare
@@ -275,6 +281,21 @@ module Base = | |||
let destabilize_ref: (S.v -> unit) ref = ref (fun _ -> failwith "no destabilize yet") in | |||
let destabilize x = !destabilize_ref x in (* must be eta-expanded to use changed destabilize_ref *) | |||
|
|||
let format_wpoint x = Option.map_default (fun x -> Printf.sprintf "true (gas: %d)" x) "false" (HM.find_option wpoint_gas x) in |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
If this is just used for tracing, then something like the following would be nicer:
let format_wpoint x = Option.map_default (fun x -> Printf.sprintf "true (gas: %d)" x) "false" (HM.find_option wpoint_gas x) in | |
let pretty_wpoint () x = | |
match HM.find_option wpoint_gas x with | |
| None -> Pretty.text "false" | |
| Some x -> Pretty.dprintf "true (gas: %d)" x | |
in |
(Don't commit this directly, I have not typechecked this.)
That can be used in tracing with %a
. It avoids the lookup and string construction if tracing is enabled but something other is being traced. Otherwise unnecessary garbage is created.
else if term then | ||
match phase with | ||
| Widen -> S.Dom.widen old (S.Dom.join old eqd) | ||
| Narrow when GobConfig.get_bool "exp.no-narrow" -> old (* no narrow *) | ||
| Narrow -> | ||
(* assert S.Dom.(leq eqd old || not (leq old eqd)); (* https://github.com/goblint/analyzer/pull/490#discussion_r875554284 *) *) | ||
S.Dom.narrow old eqd | ||
else | ||
box old eqd | ||
else ( | ||
if term then | ||
match phase with | ||
| Widen -> S.Dom.widen old (S.Dom.join old eqd) | ||
| Narrow when GobConfig.get_bool "exp.no-narrow" -> old (* no narrow *) | ||
| Narrow -> | ||
(* assert S.Dom.(leq eqd old || not (leq old eqd)); (* https://github.com/goblint/analyzer/pull/490#discussion_r875554284 *) *) | ||
S.Dom.narrow old eqd | ||
else ( | ||
box old eqd | ||
) | ||
) |
There was a problem hiding this comment.
Choose a reason for hiding this comment
The reason will be displayed to describe this comment to others. Learn more.
This seems to be just changing the formatting which would be nice to not change unnecessarily.
Description
This PR adds widening gas to the td3 solver. Widening gas permits an unknown to grow N times and still be combined with the regular join. Only once the unknown runs out of gas, the widening operator is used. Widening gas can be applied to regular unknowns and leaf unknowns.
Additionally, this PR extracts the side_widen strategies out of the main solve routine and wraps them in an interface.
Config Options
The gas budget for regular unknowns is controlled via
solvers.td3.widen_gas
, whereas leaves are controlled viasolvers.td3.side_widen_gas
. Both config options are 0 by default, effectively disabling the feature.When widening points are configured to be reset with the
solvers.td3.remove-wpoint
option, this too resets that wpoints gas.New side_widen Interface and Gas for Leaves
Widening gas co-exists with the other side-widening strategies.
Side widen strategies are meant to determine, when a leaf becomes a widening point. After a leaf has been selected as widening point, future updates to its value are applied with the widen operator. The widening gas can again be used to postpone this:
After a leaf has been marked as widening point, its gas counter is decreased for every increase to its value. Once it hits 0, widening is applied.
There is one exception, namely the side_widen strategy
sides-local
. It does not mark widening points, instead widening only if the incoming side-effect is larger than a previous side-effect that came from the same unknown. This has been adapted to work with widening gas like so: every time that thesides-local
strategy would widen, the gas is decremented.Functions of the new interface:
Note that
notify_side
is needed forunstable-self
, which records side-effects in theinfl
set.veto_widen
is necessary to implement the above behavior forsides-local
.record_destabilized_vs
is currently only used by thecycle
strategy. It indicates, whether the more expensive version ofdestabilize
needs to be called, which also records whether a called or start variable was destabilized.